import * as THREE from '../../build/three.module.js';
import {
  OrbitControls
} from '../../jsm/controls/OrbitControls.js';

const scene = new THREE.Scene();

/**
 * 创建骨骼网格模型 SkinnedMesh
 */
// 圆柱几何体y坐标范围[-60, 60]
const skinnedGeo = new THREE.CylinderBufferGeometry(5, 5, 120, 20, 20); // 竖直分片量要大，否则没有足够的顶点进行变换
skinnedGeo.translate(0, 60, 0); // 上移60，y坐标范围[0, 120]

/**
 * 设置几何体对象的蒙皮索引 skinIndices、蒙皮权重 skinWeights 属性
 * 实现一个模拟腿部骨骼运动的效果
 */
// 遍历顶点，为每个顶点设置蒙皮索引和蒙皮权重
// 权重根据y值来分段：0-60，60-100,100-120
const vertex = new THREE.Vector3();
const skinIndices = [];
const skinWeights = [];
console.log(skinnedGeo);
for(let i = 0; i < skinnedGeo.attributes.position.count; i += 1) {
  // const vertex = skinnedGeo.vertices[i];
  vertex.fromBufferAttribute(skinnedGeo.attributes.position, i);
  if (vertex.y <= 60) {
    // 设置顶点受关节 Bone1 影响
    skinIndices.push(0, 0, 0, 0);
    // 设置顶点蒙皮权重 1 - vertex.y / 60
    skinWeights.push(1 - vertex.y / 60, 0, 0, 0);
  } else if (vertex.y > 60 && vertex.y <= 100) {
    skinIndices.push(1, 0, 0, 0); // 受第二个关节 Bone2 影响
    skinWeights.push(1 - (vertex.y - 60) / 40, 0, 0, 0);
  } else if (vertex.y > 100 && vertex.y <= 120) {
    skinIndices.push(2, 0, 0, 0);
    skinWeights.push(1 - (vertex.y - 100) / 20, 0, 0, 0);
  }
}
skinnedGeo.setAttribute('skinIndex', new THREE.Uint16BufferAttribute(skinIndices, 4));
skinnedGeo.setAttribute('skinWeight', new THREE.Float32BufferAttribute(skinWeights, 4));

// 材质
const skinnedMat = new THREE.MeshBasicMaterial({
  skinning: true, // 启用蒙皮动画
  color: 0x0000ff,
  wireframe: true // 辅助线
});

// 创建骨骼网格模型
const skinnedMesh = new THREE.SkinnedMesh(skinnedGeo, skinnedMat); // SkinnedMewsh 只支持 BufferGeometry
skinnedMesh.position.set(50, 120, 50);
skinnedMesh.rotateX(Math.PI);

/**
 * 骨骼系统
 */
// 创建 3 个关节实例
const bone1 = new THREE.Bone(); // 关节1，根关节
const bone2 = new THREE.Bone(); // 关节2
const bone3 = new THREE.Bone(); // 关节3
// 设置父子关节关系，构成关节树
bone1.add(bone2);
bone2.add(bone3);
// 设置关节之间的相对位置
bone2.position.y = 60; // bone2 相对 bone1 的位置
bone3.position.y = 40; // bone3 相对 bone2 的位置

// 将关节插入到骨骼系统中
const skeleton = new THREE.Skeleton([bone1, bone2, bone3]);

// 将骨骼系统关联到骨骼网格模型
skinnedMesh.add(skeleton.bones[0]); // 添加根关节
skinnedMesh.bind(skeleton); // 网格模型绑定骨骼系统
console.log(skinnedMesh);
skeleton.bones[1].rotation.x = 0.5;
skeleton.bones[2].rotation.x = 0.5;

scene.add(skinnedMesh);

// 显示骨骼动画辅助线
const skeletonH = new THREE.SkeletonHelper(skinnedMesh);
scene.add(skeletonH);

const al = new THREE.AmbientLight(0xffffff);
scene.add(al);

const axesH = new THREE.AxesHelper(300);
scene.add(axesH);

const k = innerWidth / innerHeight,
      s = 60,
      camera = new THREE.PerspectiveCamera(s, k, 1, 10000);
camera.position.set(400, 400, 400);
camera.lookAt(scene.position);

const renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0xaaaaaa);
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);

new OrbitControls(camera, renderer.domElement);

/**
 * 骨骼动画效果
 */
let n = 0;
let T = 50;
let step = .01;
const skeletonAnimation = () => {
  n += 1;
  if (n < T) {
    // 改变关节角度
    skeleton.bones[0].rotation.x = skeleton.bones[0].rotation.x - step;
    skeleton.bones[1].rotation.x = skeleton.bones[1].rotation.x + step;
    skeleton.bones[2].rotation.x = skeleton.bones[2].rotation.x + 2 * step;
  }
  if (n < 2 * T && n > T) {
    skeleton.bones[0].rotation.x = skeleton.bones[0].rotation.x + step;
    skeleton.bones[1].rotation.x = skeleton.bones[1].rotation.x - step;
    skeleton.bones[2].rotation.x = skeleton.bones[2].rotation.x - 2 * step;
  }
  if (n === 2 * T) {
    n = 0;
  }
}

(function render () {
  renderer.render(scene, camera);
  requestAnimationFrame(render);

  skeletonAnimation();
})();